#include "llvm/ADT/APInt.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/ExecutionEngine/GenericValue.h"
#include "llvm/ExecutionEngine/MCJIT.h"
#include "llvm/IR/Argument.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/InstrTypes.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/raw_ostream.h"
#include <cstdio>

using namespace llvm;

extern "C" {
using u8 = unsigned char;
class RustLLVMContextRef;
class RustLLVMFunctionRef;
class RustStringRef;
class RustLLVMModuleRef;
class RustLLVMFuncTypeRef;
class RustLLVMTypeRef;
class RustLLVMIntegerTypeRef;
class RustLLVMFunctionTypeRef;
}

llvm::LLVMContext &get_context_ref(RustLLVMContextRef ctx);

char *get_cstr(RustStringRef ref);

extern "C" {
class RustLLVMTypeRef {
public:
    void *ty;

    RustLLVMTypeRef(void *ty) : ty(ty) {}
};

class RustLLVMIntegerTypeRef : RustLLVMTypeRef {
public:
    RustLLVMIntegerTypeRef(void *ty) : RustLLVMTypeRef(ty) {}
};

class RustLLVMFunctionTypeRef : RustLLVMTypeRef {
public:
    RustLLVMFunctionTypeRef(void *ty) : RustLLVMTypeRef(ty) {}
};

class RustLLVMFunctionRef {
public :
    void *f;
};

class RustStringRef {
public:
    void *s;
    size_t length;

    RustStringRef(char *cstr) {
        this->s = cstr;
        this->length = strlen(cstr);
    }
};

class RustLLVMModuleRef {
public:
    void *m;

    RustLLVMModuleRef(RustStringRef module_id, RustLLVMContextRef context);
};

class RustLLVMContextRef {
public:
    void *ctx;

    RustLLVMContextRef();

    RustLLVMIntegerTypeRef get_int32_type();
};

RustLLVMIntegerTypeRef RustLLVMContextRef::get_int32_type() {
    return RustLLVMIntegerTypeRef(0);
//    std::cout << "00 \n";
//    LLVMContext &c = get_context_ref(*this);
//    std::cout << "11 \n";
//    IntegerType* ty = Type::getInt32Ty(c);
//    return RustLLVMIntegerTypeRef(ty);
}

RustLLVMContextRef::RustLLVMContextRef() {
    this->ctx = new llvm::LLVMContext();
}

RustLLVMModuleRef::RustLLVMModuleRef(RustStringRef module_id, RustLLVMContextRef context) {
    llvm::LLVMContext &c = get_context_ref(context);
    char* s = get_cstr(module_id);
//    throw "";
    this->m = new Module("", c);
}

void RustLLVMInitializeNativeTarget() {
    InitializeNativeTarget();
}

void RustLLVMInitializeNativeTargetAsmPrinter() {
    InitializeNativeTargetAsmPrinter();
}

}


llvm::LLVMContext &get_context_ref(RustLLVMContextRef ctx) {
    return (llvm::LLVMContext &) ctx.ctx;
}

char *get_cstr(RustStringRef ref) {
    return (char *) ref.s;
}
